package com.googlecode.archimedes.filegen.model;

import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * Specifies a relationship between two AbstractType classes (i.e. Entity or Interface)
 * 
 * @author John Lema, Erik Gustavson
 * @version $Id: Relationship.java 2088 2006-03-16 01:56:24 +0000 (Thu, 16 Mar 2006) erik $
 *
 * @TODO Add method comments & document class
 */

public class Relationship extends AbstractAsset implements Inheritable, Comparable<Relationship> {

    private AbstractType fromEntity;                            //TODO Rename this to SourceEntity
    private AbstractType toEntity;                              //TODO Rename this to TargetEntity
    private String       aliasSource;
    private String       aliasTarget;
    private Association  association;
    private Direction    direction    = Direction.BiDirectional;
    private String       name;
    private String       displayName;
    private boolean      internalized = false;                  //TODO eliminate this boolean in favor of "internal" only
    private Integer      order        = 0;
    private String       restrictBeanPath;
    /*
     * Attributes used for parsing-only. These are used to set the above attributes during build time. WARNING: DO NOT USE THEM OTHERWISE.
     */
    private String       type;
    private String       target;
    private String       display;
    private boolean      internal;
    private String       dir          = "bi";



    /**
     * @return
     */
    public AbstractType getFromEntity() {
        return fromEntity;
    }

    /**
     * @param fromEntity
     */
    public void setFromEntity(AbstractType fromEntity) {
        this.fromEntity = fromEntity;
    }

    /**
     * @return
     */
    public AbstractType getToEntity() {
        return toEntity;
    }

    /**
     * @param toEntity
     */
    public void setToEntity(AbstractType toEntity) {
        this.toEntity = toEntity;
    }

    /**
     * @deprecated Use getAssociation() instead
     * @return
     */

    /**
     * * Sets the association of the relationship
     * These values are... 
     * 0 = One-to-many
     * 1 = many-to-one
     * 2 = many-to-many
     * 3 = one-to-one
     * 
     * @param asociation - the association to set, OTM=0, MTO=1 MTM=2 OTO=3 
     */
    public void setAssociation(Association asociation) {
        this.association = asociation;
    }

    /**
     * @return
     */
    public Association getAssociation() {
        return this.association;
    }

    /**
     * Sets the direction of the relationship
     * These values are... 
     * O = UNI_DIRECTION
     * 1 = UNI_DIRECTION
     * 
     * @param direction - the direction to set, uni=0, bi = 1. 
     */
    public void setDirection(Direction direction) {
        this.direction = direction;
    }

    /**
     * Gets the direction of the relationship
     * These values are... 
     * O = UNI_DIRECTIONAL
     * 1 = UNI_DIRECTIONAL
     * 
     * @param direction -the direction to set, uni=0, bi = 1. 
     */
    public Direction getDirection() {
        return direction;
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }

    /**
     * @param name The name to set. Needs to be unique to the AbstractType-AbstractType relationship
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Returns the displayName.
     *
     * @return String value of displayName
     */
    public String getDisplayName() {
        return displayName;
    }

    /**
     * Sets the displayName
     * @param displayName The displayName to set.
     */
    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    /**
     * Returns the internalized.
     *
     * @return boolean value of internalized
     */
    public boolean getInternalized() {
        return internalized;
    }

    /**
     * Sets the internalized
     * @param internalized The internalized to set.
     */
    public void setInternalized(boolean internalized) {
        this.internalized = internalized;
    }

    /**
     * Returns the From and To entity from the relationship.
     * 
     * @see com.tps.filegen.model.Asset#getSubordinateAssets()
     */
    public Collection<? extends Asset> getSubordinateAssets() {
        Collection<Asset> collection = new ArrayList<Asset>();
        collection.add(getFromEntity());
        collection.add(getToEntity());

        return collection;
    }

    /** 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 29).append(name).toHashCode();
    }

    /** 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {

        if (obj != null && obj instanceof Relationship) {
            Relationship other = (Relationship) obj;
            return this.name.equals(other.name);
        }

        return false;
    }

    /**
     * Returns the order.
     *
     * @return Integer value of order
     */
    public Integer getOrder() {
        return order;
    }

    /**
     * Sets the order
     * @param order The order to set.
     */
    public void setOrder(Integer order) {
        this.order = order;
    }

    /**
     * Returns the aliasSource. Defaults to the source Entity's natural name if not specified.
     *
     * @return String value of aliasSource
     */
    public String getAliasSource() {
        return aliasSource != null ? aliasSource : getFromEntity().getEntityName();
    }

    /**
     * Sets the aliasSource
     * @param aliasSource The aliasSource to set.
     */
    public void setAliasSource(String aliasSource) {
        this.aliasSource = aliasSource;
    }

    /**
     * Returns the aliasTarget. Defaults to the target Entity's natural name if not specified.
     *
     * @return String value of aliasTarget
     */
    public String getAliasTarget() {
        return aliasTarget != null ? aliasTarget : getToEntity().getEntityName();
    }

    /**
     * Sets the aliasTarget
     * @param aliasTarget The aliasTarget to set.
     */
    public void setAliasTarget(String aliasTarget) {
        this.aliasTarget = aliasTarget;
    }

    /** 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(getFromEntity());
        sb.append("] ");
        sb.append(getDisplay());
        sb.append(" [");
        sb.append(getToEntity());
        sb.append("] (");
        sb.append(getAssociation());
        sb.append(")");

        return sb.toString();
    }

    /** 
     * @see java.lang.Comparable#compareTo(T)
     */
    public int compareTo(Relationship obj) {
        Relationship lhs = this;
        Relationship rhs = obj;
        return lhs.getOrder().compareTo(rhs.getOrder());
    }

    /**
     * Returns the display.
     *
     * @return String value of display
     */
    public String getDisplay() {
        return display;
    }

    /**
     * Sets the display
     * @param display The display to set.
     */
    public void setDisplay(String display) {
        this.display = display;
    }

    /**
     * Returns the internal.
     *
     * @return boolean value of internal
     */
    public boolean isInternal() {
        return internal;
    }

    /**
     * Sets the internal
     * @param internal The internal to set.
     */
    public void setInternal(boolean internal) {
        this.internal = internal;
    }

    /**
     * Returns the target.
     *
     * @return String value of target
     */
    public String getTarget() {
        return target;
    }

    /**
     * Sets the target
     * @param target The target to set.
     */
    public void setTarget(String target) {
        this.target = target;
    }

    /**
     * Returns the type.
     *
     * @return String value of type
     */
    public String getType() {
        return type;
    }

    /**
     * Sets the type
     * @param type The type to set.
     */
    public void setType(String type) {
        this.type = type;
    }

    /**
     * Returns the dir.
     *
     * @return String value of dir
     */
    public String getDir() {
        return dir;
    }

    /**
     * Sets the dir
     * @param dir The dir to set.
     */
    public void setDir(String dir) {
        this.dir = dir;
    }

    /**
     * Returns the restrictBeanPath.
     *
     * @return String value of restrictBeanPath
     */
    public String getRestrictBeanPath() {
        return restrictBeanPath;
    }

    /**
     * Sets the restrictBeanPath
     * @param restrictBeanPath The restrictBeanPath to set.
     */
    public void setRestrictBeanPath(String restrictBeanPath) {
        this.restrictBeanPath = restrictBeanPath;
    }

}